import time
import uuid
import secrets
from datetime import datetime

from django.conf import settings
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from dotenv import load_dotenv  # 需安装:pip install cryptography python-dotenv requests
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from jewelry import models
from wechatpayv3 import WeChatPay, WeChatPayType  # 需要安装:pip install wechatpayv3

from ..serializers import OrderInfoSerializer, OrderStatusSerializer

from django.db import transaction
from django.db.models import F
from django.core.exceptions import ValidationError

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
import xml.etree.ElementTree as ET

from rest_framework.permissions import IsAuthenticated
from django.shortcuts import get_object_or_404

# 安装 django-redis（核心库）
# pip install django-redis
# 安装 redis-py（Python 的 Redis 客户端，必选依赖）
# pip install redis
# 配置settings.py
from django_redis import get_redis_connection
from rest_framework.permissions import AllowAny

wx_pay = WeChatPay(
    cert_serial_no=settings.WECHAT_SERIAL_NO,  # 商户序列号
    private_key=settings.WECHAT_PRIVATE_KEY,  # 商户私钥
    cert_dir=settings.CERT_DIR,  # 证书存放目录
    mchid=settings.WECHAT_MCHID,  # 商户号
    appid=settings.WECHAT_APPID,  # 应用id
    apiv3_key=settings.WECHAT_API3_KEY,  # 商户APIv3密钥
    wechatpay_type=WeChatPayType.MINIPROG,  # 微信支付类型
    notify_url=settings.WECHAT_NOTIFY_URL + 'api/paymentV3/callback/'  # 支付结果回调地址
)


class PaymentV3View(APIView):
    # permission_classes = [IsAuthenticated]  # 验证用户是否有权限访问此接口
    permission_classes = [AllowAny]  # 允许匿名访问

    def post(self, request):
        user = request.user
        serializer = OrderInfoSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)  # 如果数据无效，将引发验证错误
        if serializer.is_valid():
            serializerData = serializer.validated_data['orderData']
            orderAddress = serializerData['address']  # 收货地址
            orderGoods = serializerData['goods']  # 订单商品信息
            total = int(serializerData['total'] * 100)  # 要付款的总金额(以下必须要的)
            mobile = serializerData['mobile']  # 登录客户手机号(用的手机号登录)
            payment = serializerData['payment']  # 微信支付类型(暂没用到)

            # print('orderAddress:', orderAddress)
            # print('orderGoods:', orderGoods)
            # print('total:', total)
            # print('mobile:', mobile)
            # print('payment:', payment)

            order_no = str(uuid.uuid4())[:20]  # +时间11位 商户系统内部订单号(以下必须要的,最大32位)
            # 用户对象.用的是手机号登录,就用手机号寻找用户对象user_obj
            user_obj = models.UserInfo.objects.filter(phone=mobile).first()  # 里面有openid
            openid = user_obj.openid

            try:
                # 创建微信支付订单
                out_trade_no = f"{order_no}_{int(time.time())}"
                code, message = wx_pay.pay(
                    description='测试',
                    out_trade_no=out_trade_no,
                    amount={'total': total, 'currency': 'CNY'},
                    payer={'openid': openid},
                    notify_url=settings.WECHAT_NOTIFY_URL + 'api/paymentV3/callback/',
                    # pay_type=WeChatPayType.JSAPI
                )

                if code in [200, 201]:
                    # 在地址表中增加收货地址(使用get_or_create方法实现原子操作,避免并发场景下的重复插入)
                    address_obj, created = models.Address.objects.get_or_create(
                        user=user_obj,  # 关联用户
                        receiver=orderAddress['userName'],  # 收货人姓名
                        mobile=orderAddress['telNumber'],  # 手机号
                        province=orderAddress['province'],  # 省
                        city=orderAddress['city'],  # 市
                        district=orderAddress['county'],  # 区县
                        detail=orderAddress['detail'],  # 详细地址
                        defaults={'is_default': orderAddress['isDefault']}  # 默认地址标识
                    )
                    # 在订单主表中增加订单记录
                    order_obj = models.SalesOrder.objects.create(
                        order_no=out_trade_no,  # 订单号
                        user=user_obj,  # 关联用户
                        address=address_obj,  # 关联地址
                        total_amount=total / 100,  # 订单总额
                        status='unpaid'  # 订单状态：'待支付'
                    )
                    # 在订单明细表中增加订单明细
                    for sku in orderGoods:
                        models.OrderItem.objects.create(
                            order=order_obj,  # 关联订单主表
                            sku_id=sku['id'],  # 关联sku
                            quantity=sku['count'],  # 购买数量
                            unit_price=sku['price'],  # 成交单价
                            total_price=sku['count'] * sku['price']  # 小计金额
                        )

                    # 生成 timeStamp（时间戳）
                    timeStamp = str(int(time.time()))  # 秒级时间戳，转为字符串
                    # 生成 nonceStr（随机字符串）
                    nonce_str = secrets.token_hex(16)  # 32 位随机字符串（16字节的hex）

                    # 生成 paySign（支付签名）
                    load_dotenv()
                    # 从settings中读取私钥内容
                    private_key_str = settings.WECHAT_PRIVATE_KEY
                    private_key = serialization.load_pem_private_key(
                        private_key_str.encode(),
                        password=None,
                        backend=default_backend()
                    )
                    # 构造签名串
                    import ast
                    message_data = ast.literal_eval(message)
                    prepay_id = message_data['prepay_id']  # 不能用.取值!!!
                    sign_str = f"{settings.WECHAT_APPID}\n{timeStamp}\n{nonce_str}\nprepay_id={prepay_id}\n"

                    # 使用 SHA256-RSA 签名
                    from cryptography.hazmat.primitives import hashes
                    from cryptography.hazmat.primitives.asymmetric import padding
                    signature = private_key.sign(
                        sign_str.encode(),
                        padding.PKCS1v15(),
                        hashes.SHA256()
                    )
                    # Base64 编码签名结果
                    import base64
                    pay_sign = base64.b64encode(signature).decode()

                    message_data['timeStamp'] = timeStamp
                    message_data['nonce_str'] = nonce_str
                    message_data['pay_sign'] = pay_sign
                    message_data['out_trade_no'] = out_trade_no
                    return Response(message_data, status=status.HTTP_200_OK)
                else:
                    return Response({'error': message}, status=status.HTTP_400_BAD_REQUEST)
            except Exception as e:
                return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


@method_decorator(csrf_exempt, name='dispatch')
class PaymentCallbackView(APIView):
    """ 支付成功由腾讯发来通知,根据返回结果修改订单状态 """

    def post(self, request):
        try:
            data = wx_pay.callback(request.headers, request.body)
            if data and data.get('event_type') == 'TRANSACTION.SUCCESS':
                # print('V3版本data:', data)  # 里面返回来非常多的数据,需要查了用
                # 1. 获取事务ID确保幂等性
                transaction_id = data['resource']['transaction_id']
                if models.SalesOrder.objects.filter(transaction_id=transaction_id).exists():
                    return Response({'message': 'OK'})  # 已处理过的请求直接返回

                with transaction.atomic():  # 开启事务
                    try:
                        # 3. 获取并锁定订单（悲观锁）
                        order = models.SalesOrder.objects.select_for_update().get(
                            order_no=data['resource']['out_trade_no'],
                            status='unpaid',  # '待支付'
                        )

                        # 4. 更新订单状态
                        order.status = 'paid'  # '已支付'
                        order.transaction_id = transaction_id
                        order.payment_time = data['resource']['success_time']

                        # 5. 处理库存扣减
                        for item in order.items.select_related('sku').select_for_update():
                            # 双重检查库存
                            if item.sku.stock < item.quantity:
                                raise ValidationError(f"SKU {item.sku.id} 库存不足")

                            # 原子操作扣减库存（乐观锁机制）
                            updated = models.SKU.objects.filter(
                                id=item.sku.id,
                                stock__gte=item.quantity
                            ).update(
                                stock=F('stock') - item.quantity
                            )

                            if not updated:
                                raise ValidationError(f"SKU {item.sku.id} 库存更新失败")

                        # 写入Redis缓存（有效期10分钟）
                        redis = get_redis_connection('payment')
                        redis.setex(f'payment:{order.id}', 600, 'paid')  # 1='PAID'

                        # 6. 提交所有变更
                        order.save()

                    except models.SalesOrder.DoesNotExist:
                        return Response({'code': '404', 'message': '订单不存在'})
                        # return HttpResponse("订单不存在", status=404)
                    except ValidationError as e:
                        # 7. 异常处理（记录日志+告警）
                        # logger.error(f"库存扣减失败: {e}")
                        # 8. 发送告警到管理后台
                        # send_alert_to_admin(order)
                        transaction.set_rollback(True)
                        return Response({'code': '409', 'message': '库存不足'})
                        # return HttpResponse("库存不足", status=409)

                # 更新订单信息为已支付
                # order = models.SalesOrder.objects.get(order_no=data['resource']['out_trade_no'])
                # order.status = 1
                # order.transaction_id = data['resource']['transaction_id']
                # order.pay_time = data['resource']['success_time']
                # order.save()
                # 同步更新库存

                print('V3版本--支付成功!', datetime.today())
                return Response({'code': 'SUCCESS'})
            print('注意:V3版本--支付失败了!!!', datetime.today())
            return Response({'code': 'FAIL', 'message': 'Invalid callback'})
        except Exception as e:
            print('注意:V3版本--支付代码里有错误!!!', datetime.today())
            return Response({'code': 'FAIL', 'message': str(e)})


# # ‌退款实现要点‌：
# class RefundOrder(APIView):
#     def post(self, request):
#         # 必须校验用户权限和订单归属
#         if not request.user.has_perm('order.refund'):
#             return Response(status=403)
#
#         # 调用微信退款API
#         wechatpay.request("POST", "refund/domestic/refunds", data)


class OrderStatusView(APIView):
    """订单状态查询, 暂时未用到"""

    def get(self, request):
        out_trade_no = request.query_params.get('out_trade_no')
        try:
            order = models.SalesOrder.objects.get(order_no=out_trade_no)
            # 优先读取Redis缓存
            redis = get_redis_connection('payment')
            cached_status = redis.get(f'payment:{order.id}')
            if cached_status:
                order.status = cached_status.decode()
            return Response(OrderStatusSerializer(order).data)
        except models.SalesOrder.DoesNotExist:
            return Response({'error': '订单不存在'}, status=404)

